package jmt.engine.NetStrategies.RoutingStrategies;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import jmt.common.exception.IncorrectDistributionParameterException;
import jmt.common.exception.LoadException;
import jmt.common.exception.NetException;
import jmt.engine.NetStrategies.RoutingStrategy;
import jmt.engine.QueueNet.Job;
import jmt.engine.QueueNet.JobClass;
import jmt.engine.QueueNet.NetNode;
import jmt.engine.QueueNet.NodeList;
import jmt.engine.QueueNet.NodeSection;
import jmt.engine.random.Empirical;
import jmt.engine.random.EmpiricalEntry;
import jmt.engine.random.EmpiricalPar;
import jmt.gui.common.routingStrategies.EnergyAwareRouting;

public class EnergyAwareRoutingStrategy extends RoutingStrategy {
	private int CLOSED_CLASS = JobClass.CLOSED_CLASS;
	private double windowSize;
	private double age;
	private NetNode waitingQueue = new NetNode("waitingQueue");
	private List<Double> betas = new ArrayList<Double>();
	private double optimalMix;
	private EnergyAwareRoutingParameter param;
	private byte inputSection = NodeSection.INPUT;
	private byte serviceSection = NodeSection.SERVICE;
	private int property = NodeSection.PROPERTY_ID_RESIDENT_JOBS; 
    private Empirical distribution;
	private double probabilities[];
	private String[] paramNames;
	
	public EnergyAwareRoutingStrategy(EnergyAwareRoutingParameter[] params) {
		
		param = params[0];
		
		this.optimalMix = param.getPopMix();
		this.age = param.getAge();
		this.windowSize = param.getWinSize();
	}
	
	/*private NetNode sendJobForWaitingQueue(NodeList nodes, JobClass jobClass){
		try{
			if(betas.size()>0) {
				betas.clear();
			}
			System.out.println("Lavoro con la classe "+jobClass.getName());
			for(int i=0; i<nodes.size(); i++) {
					double opt = optimalMix;
					double nextBeta = (double)(nodes.get(i).getJobInfoList().size(jobClass)+1)/(nodes.get(i).getJobInfoList().size()+1);
					//System.out.println("Questo è il bv: "+nextBeta);
					betas.add(i, Math.abs(opt-nextBeta));
					System.out.println("Nodo "+i+" -> "+betas.get(i));
			}
			int index = -1;
			double min = 2;
			for(int i=0; i<betas.size(); i++) {
				if(min>betas.get(i)) {
					min = betas.get(i);
					index = i;
				}
			}
			System.out.println("Invio il lavoro al nodo "+index);
			return nodes.get(index);
		} catch (NetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}*/
	
	private NetNode sendJobForNode(NodeList nodes, JobClass jobClass) {
		double actualBeta = 0;
		double nextBeta = 0;
		HashMap<Integer, Double> nextDest = new HashMap<Integer, Double>();
		int retIndex = -1; //Init a -1 così so se non è stato trovato nessun nodo buono!
		
		System.out.println("---------------------------------------------------------------");
		System.out.println("Lavoro con la classe "+jobClass.getName());
		System.out.println("Optimal mix for "+jobClass.getName()+" is "+optimalMix);
		
		try{
			double opt = optimalMix;
			for(int i=0; i<nodes.size(); i++) {
				double totJobInNodeForClass = (double) nodes.get(i).getSection(inputSection).getIntSectionProperty(property, jobClass) + nodes.get(i).getSection(serviceSection).getIntSectionProperty(property, jobClass);
				double totJobInNode = (double) nodes.get(i).getSection(inputSection).getIntSectionProperty(property) + nodes.get(i).getSection(serviceSection).getIntSectionProperty(property);
				if(totJobInNode == 0.0) {
					System.out.println("Nodo vuoto. Posso inviare al nodo "+i);
					nextDest.put(i, -1.0);
				} else {
					actualBeta = totJobInNodeForClass/totJobInNode;
					nextBeta = (totJobInNodeForClass + 1)/(totJobInNode + 1);
					//actualBeta = (double)(nodes.get(i).getJobInfoList().size(jobClass))/nodes.get(i).getJobInfoList().size();
					//nextBeta = (double)(nodes.get(i).getJobInfoList().size(jobClass)+1)/(nodes.get(i).getJobInfoList().size()+1);
					if(Math.abs(opt-nextBeta) <= Math.abs(opt-actualBeta)) {
						System.out.println("|"+opt+" - "+nextBeta+"| = "+Math.abs(opt-nextBeta)+" <= |"+opt+" - "+actualBeta+"| = "+Math.abs(opt-actualBeta)+" -> Potrei inviare al nodo "+i);
						nextDest.put(i, Math.abs(opt-nextBeta));
						//return nodes.get(i);
					} else {
						System.out.println("|"+opt+" - "+nextBeta+"| = "+Math.abs(opt-nextBeta)+" > |"+opt+" - "+actualBeta+"| = "+Math.abs(opt-actualBeta)+" -> NON POSSO INVIARE AL NODO "+i);
						nextDest.put(i, 1.0);
					}
				}
				System.out.println("I job nel nodo sono "+totJobInNode);
			}
			

			double min = 1.0;
			for(int i=0; i<nextDest.size(); i++) {
				if(min > nextDest.get(i)) {
					min = nextDest.get(i);
					retIndex = i;
				}
			}
			if(retIndex != -1) {
				System.out.println("Invio al nodo "+retIndex);
				return nodes.get(retIndex);
			} else {
				System.out.println("JOB DROPPATO!");
				return waitingQueue;
			}
		} catch (NetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	private NetNode sendJob(NodeList nodes, JobClass jobClass) {
		
		return sendJobForNode(nodes, jobClass);
	}
	
	@Override
	public NetNode getOutNode(NodeList nodes, JobClass jobClass) {
		if(nodes.size() == 0) {
			return null;
		}
//		if(Math.random()*10 < 5){
//			System.out.println("Minore di 5 -> invio");
//			return nodes.get(0);
//		} else {
//			System.out.println("Maggiore di 5 -> DROPPO!");
//			return null;
//		}
		NetNode out = sendJob(nodes, jobClass);
		return out;
	}
}